// Copyright 2013 Petar Maymounkov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"vena"

	"circuit/use/e"
)

type Series []*Sketch

func (s Series) Len() int {
	return len(s)
}

func (s Series) Less(i, j int) bool {
	return s[i].Time < s[j].Time
}

func (s Series) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

type Sketch struct {
	Time   vena.Time
	Sum    float64
	Weight float32
}

type Query struct {
	MetricID vena.MetricID    // Metric identity
	MinTime  vena.Time        // Minimum time
	MaxTime  vena.Time        // Maximum time
	Match    vena.TagSet      // Set of constraining tag key-value pairs
	Project  []vena.KeyID     // Set of tag keys that will be aggregated together (not used yet)
	Agg      vena.Aggregation // Aggregation function (not used yet)
	Velocity bool             // Take first derivative of resulting time series (not used yet)
}

func (srv *Server) Query(q *Query) (Series, error) {
	if q.MinTime >= q.MaxTime {
		return nil, nil
	}
	pivot := &RowKey{MetricID: q.MetricID, Time: q.MinTime, TagSetID: 0}

	iter := srv.Server.DB.NewIterator(srv.Server.ReadAndCache)
	defer iter.Close()

	iter.Seek(pivot.Encode())
	if !iter.Valid() {
		return nil, nil
	}

	bin := make(map[vena.Time]*Sketch)

	for iter.Valid() {
		key, err := DecodeRowKey(iter.Key())
		if err != nil {
			return nil, e.Pack(err)
		}
		if key.MetricID != q.MetricID || key.Time >= q.MaxTime {
			break
		}
		value, err := DecodeRowValue(iter.Value())
		if err != nil {
			return nil, e.Pack(err)
		}
		if !matches(value.TagSet, q.Match) {
			iter.Next()
			continue
		}
		// Add sketch
		skt, present := bin[key.Time]
		if !present {
			skt = &Sketch{Time: key.Time}
			bin[key.Time] = skt
		}
		skt.Sum += value.Value
		skt.Weight += 1
		//
		iter.Next()
	}
	result := make(Series, 0, len(bin))
	for _, skt := range bin {
		result = append(result, skt)
	}
	srv.rlk.Lock()
	srv.nquery++
	srv.rlk.Unlock()
	return result, nil
}

func matches(src, query vena.TagSet) bool {
	h := make(map[vena.KeyID]vena.ValueID)
	for _, tag := range src {
		h[tag.Key] = tag.Value
	}
	for _, tag := range query {
		value, ok := h[tag.Key]
		if !ok {
			return false
		}
		if value != tag.Value {
			return false
		}
	}
	return true
}
